import { compileTemplate, TemplateCompiler, compileScript, parse, SFCTemplateCompileOptions } from '@vue/compiler-sfc';

export function stripScript(content: string, id: string) {
    const matchScriptResult = content.match(/<(script)(?:.* \bsetup\b)?[^>]*>([\s\S]+)<\/\1>/);
    const sourceCode = matchScriptResult && matchScriptResult[0] ? matchScriptResult[0].trim() : '';
    if (sourceCode) {
        const { descriptor } = parse(sourceCode);
        const result = compileScript(descriptor, { refSugar: true, id });
        return result.content;
    }
    return sourceCode;
}

export function stripStyle(content: string) {
    const matchStyleResult = content.match(/<(style)[^>]*>([\s\S]+)<\/\1>/)
    return matchStyleResult && matchStyleResult[2] ? matchStyleResult[2].trim() : ''
}

export function stripTemplate(content: string) {
    const contentWithoutScriptAndStyle = content.replace(/<(script|style)[\s\S]+<\/\1>/g, '').trim();
    return contentWithoutScriptAndStyle;
}

function pad(source: string) {
    return source
        .split(/\r?\n/)
        .map(line => `  ${line}`)
        .join('\n');
}

const templateReplaceRegex = /<template>([\s\S]+)<\/template>/g;

export function genInlineComponentText(template: string, script: string, options: any, id: string) {
    let source = template;
    if (templateReplaceRegex.test(source)) {
        source = source.replace(templateReplaceRegex, '$1');
    }
    const finalOptions: SFCTemplateCompileOptions = {
        id,
        source: `<div>${source}</div>`,
        filename: 'inline-component',
        compilerOptions: {
            mode: 'function'
        }
    };
    const compiled = compileTemplate(finalOptions);

    const hasCompiledTips = compiled.tips && compiled.tips.length;
    if (hasCompiledTips) {
        compiled.tips.forEach((tip: string) => console.warn(tip));
    }

    const hasCompiledError = compiled.errors && compiled.errors.length;
    if (hasCompiledError) {
        const errorMessage = `\n  Error compiling template:\n${pad(compiled.source)}\n` +
            compiled.errors.map(e => `  - ${e}`).join('\n') +
            '\n';
        console.error(errorMessage);
    }

    const renderFunction = `${compiled.code.replace('return function render', 'function render')}`;
    const transformedScript = transformScript(script, options);
    const componentContent = `
    (function() {
        ${renderFunction}
        ${transformedScript}
        return {
            render,
            ...democomponentExport
        }
    })()`;
    return componentContent
}

function transformScript(scriptContent: string, options: any) {
    const targetScriptContent = scriptContent.trim();
    if (!targetScriptContent) {
        return 'const democomponentExport = {}';
    }

    let transformedScript = targetScriptContent
        .replace(/export\s+default/, 'const democomponentExport =')
        .replace(/import ({.*}) from 'vue'/g, (s, s1) => `const ${s1} = Vue`)
        .replace(
            /const ({ defineComponent as _defineComponent }) = Vue/g,
            'const { defineComponent: _defineComponent } = Vue'
        );

    const replacements: any[] = options?.scriptReplaces || [];

    transformedScript = replacements.reduce<string>((target, replacement) => {
        return target.replace(replacement.searchValue, replacement.replaceValue);
    }, transformedScript);

    return transformedScript;
}
